package tagging

import (
	"time"

	"gitee.com/xuender/oils/ids"
	"gitee.com/xuender/oils/tags"
	"gitee.com/xuender/tagging/db"
	"gitee.com/xuender/tagging/pb"
	"google.golang.org/protobuf/proto"
)

// DBService 数据服务.
type DBService struct {
	db db.DB
}

// NewDBService 新建数据服务.
func NewDBService(db db.DB) *DBService {
	return &DBService{
		db: db,
	}
}

// Has 包含标签.
func (s *DBService) Has(tag string) bool {
	id := ids.Hash([]byte(tag))
	ret, _ := s.db.Has(id.Bytes(), nil)

	return ret
}

func (s *DBService) Add(tag string) *pb.Tag {
	id := ids.Hash([]byte(tag))
	if bs, err := s.db.Get(id.Bytes(), nil); err == nil {
		tag := &pb.Tag{}

		if err := proto.Unmarshal(bs, tag); err == nil {
			return tag
		}
	}

	i := s.nextIndex()
	m := time.Now().UnixNano() / 1e6
	t := &pb.Tag{Index: i, Name: tag, Created: m, Modified: m}

	if bs, err := proto.Marshal(t); err == nil {
		_ = s.db.Put(id.Bytes(), bs, nil)
		_ = s.db.Put(Bytes(i), id.Bytes(), nil)
	}

	return t
}

func (s *DBService) Tag(id ids.ID) (*pb.Tag, error) {
	if bs, err := s.db.Get(id.Bytes(), nil); err == nil {
		tag := &pb.Tag{}

		if err := proto.Unmarshal(bs, tag); err == nil {
			return tag, nil
		}
	}

	return nil, ErrNotFound
}

func (s *DBService) TagByName(tag string) (*pb.Tag, error) {
	return s.Tag(ids.Hash([]byte(tag)))
}

func (s *DBService) TagByIndex(index uint32) (*pb.Tag, error) {
	key := Bytes(index)

	bs, err := s.db.Get(key, nil)
	if err != nil {
		return nil, err
	}

	return s.Tag(ids.Bytes(bs))
}

// Tags 获取标签.
func (s *DBService) GetTags(ns, id string) tags.Tags {
	key := TagsKey(ns, id)
	if bs, err := s.db.Get(key, nil); err == nil {
		return tags.Tags(bs)
	}

	return tags.Tags{}
}

// PutTags 保存标签.
func (s *DBService) PutTags(ns, id string, ts tags.Tags) error {
	key := TagsKey(ns, id)

	return s.db.Put(key, []byte(ts), nil)
}

// nextIndex 标签序号.
func (s *DBService) nextIndex() (i uint32) {
	bs, err := s.db.Get(keyIndex, nil)
	if err == nil {
		i = Uint32(bs)
	}

	i++
	_ = s.db.Put(keyIndex, Bytes(i), nil)

	return i
}
